<template>
  <div class="tutorial-page">
    <div class="tutorial-header">
      <h1>组合式API (Composition API)</h1>
      <p>掌握Composition API，提升代码组织能力</p>
    </div>

    <div class="tutorial-content">
      <div class="section">
        <h2>什么是组合式API</h2>
        <p>组合式 API (Composition API) 是一系列 API 的集合，使我们能够使用函数而不是声明选项的方式书写 Vue 组件。它是一个概括性的术语，涵盖了以下方面的 API：</p>
        <ul>
          <li>响应式 API：例如 ref() 和 reactive()，使我们能够直接创建响应式状态、计算属性和侦听器。</li>
          <li>生命周期钩子：例如 onMounted() 和 onUnmounted()，使我们能够在组件各个生命周期阶段添加逻辑。</li>
          <li>依赖注入：例如 provide() 和 inject()，使我们能够在使用响应式 API 时，利用 Vue 的依赖注入系统。</li>
        </ul>
        
        <h3>组合式API的优势</h3>
        <ul>
          <li>更好的逻辑组织</li>
          <li>更好的逻辑复用</li>
          <li>更好的类型推导</li>
          <li>更小的生产包体积</li>
          <li>更好的性能</li>
        </ul>
      </div>

      <div class="section">
        <h2>响应式基础 API</h2>
        
        <h3>ref</h3>
        <p>接受一个内部值并返回一个响应式且可变的 ref 对象。ref 对象仅有一个 .value property，指向该内部值。</p>
        <pre><code class="language-javascript" v-pre>import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    console.log(count.value) // 0

    count.value++
    console.log(count.value) // 1
    
    return {
      count
    }
  }
}</code></pre>
        <pre><code class="language-html" v-pre>&lt;template&gt;
  &lt;div&gt;
    &lt;p&gt;{{ count }}&lt;/p&gt;
    &lt;button @click="count++"&gt;增加&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
        
        <h3>reactive</h3>
        <p>返回对象的响应式副本。响应式转换是"深层的"：会影响对象内部所有嵌套的属性。</p>
        <pre><code class="language-javascript" v-pre>import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      name: 'Vue.js',
      nested: {
        age: 20
      }
    })
    
    return {
      state
    }
  }
}</code></pre>
        <pre><code class="language-html" v-pre>&lt;template&gt;
  &lt;div&gt;
    &lt;p&gt;计数: {{ state.count }}&lt;/p&gt;
    &lt;p&gt;名称: {{ state.name }}&lt;/p&gt;
    &lt;p&gt;年龄: {{ state.nested.age }}&lt;/p&gt;
    &lt;button @click="state.count++"&gt;增加计数&lt;/button&gt;
  &lt;/div&gt;
&lt;/template&gt;</code></pre>
        
        <h3>ref vs reactive</h3>
        <table>
          <thead>
            <tr>
              <th>特性</th>
              <th>ref</th>
              <th>reactive</th>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>数据类型</td>
              <td>任何类型</td>
              <td>对象/数组</td>
            </tr>
            <tr>
              <td>访问方式</td>
              <td>.value</td>
              <td>直接访问</td>
            </tr>
            <tr>
              <td>解构</td>
              <td>保持响应性</td>
              <td>失去响应性</td>
            </tr>
          </tbody>
        </table>
      </div>

      <div class="section">
        <h2>计算属性和侦听器</h2>
        
        <h3>computed</h3>
        <pre><code class="language-javascript" v-pre>import { ref, computed } from 'vue'

export default {
  setup() {
    const firstName = ref('Foo')
    const lastName = ref('Bar')
    
    const fullName = computed(() => {
      return firstName.value + ' ' + lastName.value
    })
    
    // 可写计算属性
    const writableFullName = computed({
      get: () => {
        return firstName.value + ' ' + lastName.value
      },
      set: (newValue) => {
        const names = newValue.split(' ')
        firstName.value = names[0]
        lastName.value = names[names.length - 1]
      }
    })
    
    return {
      firstName,
      lastName,
      fullName,
      writableFullName
    }
  }
}</code></pre>
        
        <h3>watch</h3>
        <pre><code class="language-javascript" v-pre>import { ref, watch } from 'vue'

export default {
  setup() {
    const question = ref('')
    const answer = ref('Questions usually contain a question mark. ;-)')

    // 侦听单个 ref
    watch(question, async (newQuestion, oldQuestion) => {
      if (newQuestion.indexOf('?') > -1) {
        answer.value = 'Thinking...'
        try {
          const res = await fetch('https://yesno.wtf/api')
          answer.value = (await res.json()).answer
        } catch (error) {
          answer.value = 'Error! Could not reach the API. ' + error
        }
      }
    })

    return {
      question,
      answer
    }
  }
}</code></pre>
        
        <h3>watchEffect</h3>
        <pre><code class="language-javascript" v-pre>import { ref, watchEffect } from 'vue'

export default {
  setup() {
    const todoId = ref(1)
    const data = ref(null)

    watchEffect(async () => {
      const response = await fetch(
        `https://jsonplaceholder.typicode.com/todos/${todoId.value}`
      )
      data.value = await response.json()
    })

    return {
      todoId,
      data
    }
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>生命周期钩子</h2>
        <p>在组合式 API 中，生命周期钩子可以注册多个：</p>
        <pre><code class="language-javascript" v-pre>import { onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from 'vue'

export default {
  setup() {
    onBeforeMount(() => {
      console.log('组件挂载前')
    })

    onMounted(() => {
      console.log('组件挂载后')
    })

    onBeforeUpdate(() => {
      console.log('组件更新前')
    })

    onUpdated(() => {
      console.log('组件更新后')
    })

    onBeforeUnmount(() => {
      console.log('组件卸载前')
    })

    onUnmounted(() => {
      console.log('组件卸载后')
    })
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>依赖注入</h2>
        <pre><code class="language-javascript" v-pre>// 父组件
import { provide, ref } from 'vue'

export default {
  setup() {
    const location = ref('North Pole')
    const geolocation = {
      longitude: 90,
      latitude: 135
    }

    provide('location', location)
    provide('geolocation', geolocation)
  }
}

// 后代组件
import { inject } from 'vue'

export default {
  setup() {
    const userLocation = inject('location', 'The Universe')
    const userGeolocation = inject('geolocation')

    return {
      userLocation,
      userGeolocation
    }
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>组合式函数</h2>
        <p>组合式函数是封装和复用有状态逻辑的推荐方式。</p>
        
        <h3>创建组合式函数</h3>
        <pre><code class="language-javascript" v-pre>// useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const increment = () => {
    count.value++
  }
  
  const decrement = () => {
    count.value--
  }
  
  const reset = () => {
    count.value = initialValue
  }
  
  const doubleCount = computed(() => count.value * 2)
  
  return {
    count,
    increment,
    decrement,
    reset,
    doubleCount
  }
}</code></pre>
        
        <h3>使用组合式函数</h3>
        <pre><code class="language-javascript" v-pre>import { useCounter } from './useCounter.js'

export default {
  setup() {
    const { count, increment, decrement, reset, doubleCount } = useCounter(10)
    
    return {
      count,
      increment,
      decrement,
      reset,
      doubleCount
    }
  }
}</code></pre>
        
        <h3>异步状态管理组合式函数</h3>
        <pre><code class="language-javascript" v-pre>// useAsyncState.js
import { ref, shallowRef } from 'vue'

export function useAsyncState(promiseFn, initialState) {
  const state = shallowRef(initialState)
  const isLoading = ref(false)
  const error = shallowRef(null)
  
  const execute = async (...args) => {
    isLoading.value = true
    error.value = null
    
    try {
      const result = await promiseFn(...args)
      state.value = result
      return result
    } catch (err) {
      error.value = err
      throw err
    } finally {
      isLoading.value = false
    }
  }
  
  return {
    state,
    isLoading,
    error,
    execute
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>与选项式API的对比</h2>
        
        <h3>选项式API示例</h3>
        <pre><code class="language-javascript" v-pre>export default {
  data() {
    return {
      count: 0,
      firstName: 'Foo',
      lastName: 'Bar'
    }
  },
  computed: {
    fullName() {
      return this.firstName + ' ' + this.lastName
    }
  },
  methods: {
    increment() {
      this.count++
    }
  },
  watch: {
    count(newVal) {
      console.log('Count changed:', newVal)
    }
  },
  mounted() {
    console.log('Component mounted')
  }
}</code></pre>
        
        <h3>组合式API等效示例</h3>
        <pre><code class="language-javascript" v-pre>import { ref, computed, watch, onMounted } from 'vue'

export default {
  setup() {
    const count = ref(0)
    const firstName = ref('Foo')
    const lastName = ref('Bar')
    
    const fullName = computed(() => {
      return firstName.value + ' ' + lastName.value
    })
    
    const increment = () => {
      count.value++
    }
    
    watch(count, (newVal) => {
      console.log('Count changed:', newVal)
    })
    
    onMounted(() => {
      console.log('Component mounted')
    })
    
    return {
      count,
      firstName,
      lastName,
      fullName,
      increment
    }
  }
}</code></pre>
      </div>

      <div class="section">
        <h2>最佳实践</h2>
        <ul>
          <li>将相关的逻辑组织在一起</li>
          <li>使用组合式函数来封装可复用的逻辑</li>
          <li>为组合式函数命名时使用 use 前缀</li>
          <li>在组合式函数中返回一个扁平的、解构友好的值对象</li>
          <li>在组合式函数中处理副作用的清理工作</li>
        </ul>
      </div>
    </div>
  </div>
</template>

<script setup>
// 页面逻辑可以在这里添加
</script>

<style scoped>
@import '@/assets/tutorial-styles.css';

table {
  width: 100%;
  border-collapse: collapse;
  margin: 20px 0;
}

table th,
table td {
  border: 1px solid #ddd;
  padding: 12px;
  text-align: left;
}

table th {
  background-color: #f2f2f2;
  font-weight: bold;
}

table tr:nth-child(even) {
  background-color: #f9f9f9;
}
</style>